% Copyright 2012 Beatrice Florat, beatrice.florat <at> googlemail <dot> com

% This file is part of Einfacher Chunk Parser.

% Einfacher Chunk Parser is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.

% Einfacher Chunk Parser is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.

% You should have received a copy of the GNU General Public License
% along with Einfacher Chunk Parser. If not, see <http://www.gnu.org/licenses/>.


% tripel_struct(+Chunk, -Struct) :-
%   ruft tripel_struct/3 auf ohne die Verwendung der
%   Variable ZeitStruct

tripel_struct(Chunk,Struct) :-
   tripel_struct(Chunk,Struct,_).


% zeit_struct(+Chunk, -ZeitStruct) :-
%   ruft tripel_struct/3 auf ohne die Verwendung der
%   Variable Struct

zeit_struct(Chunk,Zeitstruct) :-
   tripel_struct(Chunk,_,Zeitstruct).


% tripel_struct(+Chunk, -Struct, -ZeitStruct) :-
%   verbindet die Prädikate formatierung/2, tripels/2 und ausgabe/2 miteinander,
%   zuerst Chunk in gewisses Format modifizieren,
%   dann die Tripel in Subjekt, Praedikat und Objekt klassifizieren,
%   zum Schluss die Tripel in einem Struct und ZeitStruct zusammen fuehren

tripel_struct(Chunk,Struct,Zeitstruct) :-
   formatierung(Chunk,ChunkMod,Zeitform),
   tripels(ChunkMod,Tripel),
   ausgabe(Tripel,Zeitform,Struct,Zeitstruct).


% formatierung(+Chunk, -FormatChunk, -Zeitform) :-
%   formatiert die einzelnen Elemente der Liste von Chunks und 
%   gibt die formatierte Liste als FormatChunks zurueck,
%   gibt die Zeitform der Chunk-Liste zurueck

% fuer PP mit anschliessender VP
%  --> VP faellt weg
formatierung([chunk(PP),chunk(VP)|Chunks],[pp(Praeposition)|List],Zeitform) :-
   PP =.. [pp|P],
   VP =.. [vp|[V]],
   V \= verbpartikel(_),
   praeposition(P,[],[],Praeposition),
   !,
   formatierung(Chunks,List,Zeitform).

% fuer PP mit anschliessenden zwei VP (Futur II)
%  --> VPs fallen weg
formatierung([chunk(PP),chunk(VP1),chunk(VP2)|Chunks],[pp(VPStruct)|List],Zeitform) :-
   PP =.. [pp|P],
   VP1 =.. [vp|V1], VP2 =.. [vp|V2],
   V1 = infinit(_), V2 = infinit(_),
   praeposition(P,[],[],Praeposition),
   !,
   flatten_chunk(chunk(V2),[VFlat]),
   VPStruct =.. [VFlat|[Praeposition]],
   formatierung(Chunks,List,Zeitform).

% fuer PP
formatierung([chunk(PP)|Chunks],[pp(Praeposition)|List],Zeitform) :-
   PP =.. [pp|P],
   praeposition(P,[],[],Praeposition),
   !,
   formatierung(Chunks,List,Zeitform).

% fuer NP
formatierung([chunk(NP)|Chunks],[np(Nomen)|List],Zeitform) :-
   NP =.. [np|N],
   nominal(N,[],Nomen),
   !,
   formatierung(Chunks,List,Zeitform).

% fuer VP
% nur finites oder infinites Verb gefunden
formatierung([chunk(VP)|Chunks],[vp(Infinitiv)|List],Zeitform) :-
   VP =.. [vp|[V]], (V =.. [v|[F]] ; V = infinit(inf(F))),
   verben(V,Inf),
   verbpartikelsuche(Chunks,Verbpartikel),
   verbpartikelanhaengen(Verbpartikel,Inf,Partikelverb),
   !,
   zeitformen(Chunks,Partikelverb,F,Infinitiv,Zeitform),   
   formatierung(Chunks,List,_).

% fuer VP
% VP besteht aus zwei Teilen
formatierung([chunk(vp(VP,Part))|Chunks],[vp(Infinitiv)|List],Zeitform) :-
   VP =.. [v|[F]],
   verben(VP,Inf),
   verbpartikelsuche(Chunks,Verbpartikel),
   verbpartikelanhaengen(Verbpartikel,Inf,Partikelverb),
   !,
   zeitformen([chunk(vp(Part))|Chunks],Partikelverb,F,Infinitiv,Zeitform),   
   formatierung(Chunks,List,_).

% restliche Chunks ignorieren
formatierung([chunk(_)|Chunks],List,Zeitform) :-
   formatierung(Chunks,List,Zeitform).

% nicht-Chunks ignorieren
formatierung([_|Chunks],List,Zeitform) :-
   formatierung(Chunks,List,Zeitform).

% Abbruchbedingung: Liste ist leer
formatierung([],[],_).


% praeposition(+PP, -Nomenliste, -Praeposition, -Struct) :-
%   nimmt aus der Liste PP die Praeposition und speichert diese in eine Variable,
%   nimmt die Nomen und speichert diese in eine Liste
%   und fuegt diese als Struct aneinander

% Praeposition speichern
praeposition([p(P)|List],Nomen,_,Praeposition) :-
   praeposition(List,Nomen,P,Praeposition).

% mehrere Praepositionen
praeposition([p1(P1),NP,p2(P2)|List],Nomen,_,Praeposition) :-
   atom_concat(P1,'_',Prae1),
   atom_concat(Prae1,P2,P),
   praeposition([NP|List],Nomen,P,Praeposition).

% Nomen in Liste speichern
praeposition([NP|List],Nomen,Prae,Praeposition) :-
   % atom(Prae),
   NP =.. [np|NPL],
   nominal(NPL,[],N),
   praeposition(List,[Nomen|N],Prae,Praeposition).

% Ausgabe als Struct: Praeposition(Nomen)
praeposition([],Nomen,P,Praeposition) :-
   flatten(Nomen,N),
   Praeposition =.. [P|N].


% nominal(+NP, -Adjektive, -Nomen) :-
%   aus der Liste NP werden die Adjektive in eine Liste gespeichert und als
%   Struct mit den Nomen ausgegeben;
%   Determinierer und Konjunktionen werden ignoriert;
%   Possessivartikel werden wie Adjektive behandelt;
%   mehrere Nomen werden in eine Liste geschrieben;

% ignorieren von Determinierern (ausser Possessivartikel)
nominal([d(D)|List],Adjektive,Nomen) :-
   D =.. [Type|_],
   Type \= poss,
   nominal(List,Adjektive,Nomen).

% ignorieren von Konjunktionalphrase
nominal([konjp(_)|List],Adjektive,Nomen) :-
   nominal(List,Adjektive,Nomen).

% Possessivartikel wie Adjektive behandeln
nominal([d(AdjP)|List],Adjektive,Nomen) :-
   AdjP =.. [poss|Poss],
   nominal(List,[Adjektive|Poss],Nomen).

% Degree + Adjektiv
nominal([adjp(degree(D),adj(Adj))|List],Adjektive,Nomen) :-
   nominal(List,[Adjektive,D|Adj],Nomen).

% Adjektiv
nominal([adjp(AdjP)|List],Adjektive,Nomen) :-
   AdjP =.. [Type|Rest],
   Type = adj,
   nominal(List,[Adjektive|Rest],Nomen).

% Vorname
nominal([namen(first_name(Namen))],[],Namen).

% Nachname
nominal([namen(last_name(Namen))],[],Namen).

% Vor- und Nachname
nominal([namen(first_name(Name1),last_name(Name2))],[],Namen) :-
  atom_concat(Name1,'_',Name),
  atom_concat(Name, Name2,Namen).

% Appellative
nominal([namen(appellativ(Namen))],[],Namen).

% mehrere Adjektive in Liste;
% ein Adjektiv so ausgeben
nominal([n(N)],Adj,Nomen) :-
   Adj \= [],
   flatten(Adj,AdjP),
   ((length(AdjP,1),Nomen =.. [N|AdjP], ! );
    Nomen =.. [N|[AdjP]]).

% mehrere Nomen in Liste
% Unterscheidung der Rueckgabe nach Anzahl der Adjektive
nominal([n(N)|L],Adj,[Nomen,NL]) :-
   Adj \= [],
   flatten(Adj,AdjP),
   ((length(AdjP,1),Nomen =.. [N|AdjP], ! );
    Nomen =.. [N|[AdjP]]),
   nominal(L,[],NL).

% nur Nomen
% bzw. nur Nomen als letztes Element der Liste
nominal([n(N)],[],N).

% Pronom + weitere Elemente in der Liste
nominal([pronoun(personal(P))|List],[],[fw(P),NL]) :-
   nominal(List,[],NL).

% anderes Pronom und weitere Elemente in Liste
nominal([pronoun(Pronom)|List],[],[N,NL]) :-
   Pronom =.. [Type|[N]],
   Type \= personal,
   nominal(List,[],NL).

% nur Personalpronom
% bzw. nur Personalpronom als letztes Element der Liste
nominal([pronoun(personal(N))],[],fw(N)).

% Pronom als letztes Element der Liste
nominal([pronoun(Pronom)],[],N) :-
   Pronom =.. [Type|[N]],
   Type \= personal.


% verben(+FiniteVerbform, -Infinitiv) :-
%   gibt zur finiten Verbform den zugehoerigen Infinitiv aus

% Verben im Infinitiv
verben(infinit(inf(V)),V).

% Verben im Partizip
verben(infinit(partizip(V)),Infinitiv) :-
   partizip(Infinitiv,V).

% Verben in der finiten Form
verben(v(V),Infinitiv) :-
   infinitivform(V,Infinitiv).


% infinitivform(+FiniteVerbform, -Infinitiv) :-
%   sucht im Lexikon nach dem zugehoerigen Infinitiv

% unregelmaessige Verben
infinitivform(Verb,Infinitiv) :-
   unregel(Infinitiv,Verb).

% unregelmaessige Verben im Praeteritum
infinitivform(Verb,Infinitiv) :-
   praeteritum(Infinitiv,Verb).

% Verben mit Infinitivendung -en
infinitivform(Verb,Infinitiv) :-
   verb_en_endung(Stamm, Verb),!,
   atom_concat(Stamm, en, Infinitiv).

% Verben mit Infinitivendung -eln
infinitivform(Verb,Infinitiv) :-
   verb_eln_endung(Stamm, Verb),!,
   atom_concat(Stamm, eln, Infinitiv).

% Verben mit Infinitivendung -ern
infinitivform(Verb,Infinitiv) :-
   verb_ern_endung(Stamm, Verb),!,
   atom_concat(Stamm, ern, Infinitiv).

% Verben mit Infinitivendung -ien
infinitivform(Verb,Infinitiv) :-
   verb_ien_endung(Stamm, Verb),!,
   atom_concat(Stamm, ien, Infinitiv).


% verbpartikelsuche(+ChunkListe, -Verbpartikel) :-
%   sucht in der ChunkListe nach einem Verbpartikel und gibt diesen zurueck

% Abbruchbedingung: Partikel gefunden
verbpartikelsuche([chunk(vp(verbpartikel(Partikel)))|_],Partikel).

% rekursiver Aufruf
verbpartikelsuche([_|Chunks],Partikel) :-
   verbpartikelsuche(Chunks,Partikel).

% Abbruchbedingung: Liste leer
verbpartikelsuche([],[]).


% verbpartikelanhaengen(+Verbpartikel, +Infinitiv, -Verb) :-
%   haengt ein Verbpartikel an ein Infinitiv an, 
%   gibt das in der Variable Verb zurueck

% kein Verbpartikel
verbpartikelanhaengen([],Inf,Inf).

verbpartikelanhaengen(Verbpartikel,Inf,Verb) :-
   Verbpartikel \= [],
   atom_concat(Verbpartikel,Inf,Verb).


% zeitformen(+Chunks, +Infinitiv, +FinitesVerb, -RueckgabeVerb, -Zeitform) :-
%   gibt die Zeitform nach den Regeln der deutschen Grammatik zurueck;
%   gibt das RueckgabeVerb nach der entsprechenden Zeitform zurueck

% haben + Partizip = Perfekt
% RueckgabeVerb ist Infinitiv vom Partizip
zeitformen(Chunks,haben,F,I,perfekt) :-
   unregel(haben,F),
   infsuche(Chunks,[partizip(P)]),!,
   verben(infinit(partizip(P)),I).

% sein + Partizip = Perfekt
% RueckgabeVerb ist Infinitiv vom Partizip
zeitformen(Chunks,sein,F,I,perfekt) :-
   unregel(sein,F),
   infsuche(Chunks,[partizip(P)]),!,
   verben(infinit(partizip(P)),I).

% haben im Praeteritum + Partizip = Plusquamperfekt
% RueckgabeVerb ist Infinitiv vom Partizip
zeitformen(Chunks,haben,F,I,plusquamperfekt) :-
   praeteritum(haben,F),
   infsuche(Chunks,[partizip(P)]),!,
   verben(infinit(partizip(P)),I).

% sein im Praeteritum + Partizip = Plusquamperfekt
% RueckgabeVerb ist Infinitiv vom Partizip
zeitformen(Chunks,sein,F,I,plusquamperfekt) :-
   praeteritum(sein,F),
   infsuche(Chunks,[partizip(P)]),!,
   verben(infinit(partizip(P)),I).

% werden + Partizip + haben = Futur II
% RueckgabeVerb ist Infinitiv vom Partizip
zeitformen(Chunks,werden,_,I,futurII) :-
   infsuche(Chunks,[partizip(P),inf(haben)]),!,
   verben(infinit(partizip(P)),I).

% werden + Partizip + sein = Futur II
% RueckgabeVerb ist Infinitiv vom Partizip
zeitformen(Chunks,werden,_,I,futurII) :-
   infsuche(Chunks,[partizip(P),inf(sein)]),!,
   verben(infinit(partizip(P)),I).

% werden + Infinitiv = Futur I
% RueckgabeVerb ist Infinitiv
zeitformen(Chunks,werden,_,I,futurI) :-
   infsuche(Chunks,[inf(Inf)]),!,
   verben(infinit(inf(Inf)),I).

% werden + Partizip = Passiv
% RueckgabeVerb ist Infinitiv vom Partizip
zeitformen(Chunks,werden,_,P,passiv) :-
   infsuche(Chunks,[partizip(P)]),!.

% Praeteritum
% RueckgabeVerb ist Infinitiv von der Pareteritumform
zeitformen(Chunks,Verb,F,Verb,praeteritum) :-
   praeteritum(Verb,F),
   infsuche(Chunks,[]),!.

% Praesens
% nur ein Verb gefunden
zeitformen(Chunks,Verb,_,Verb,praesens) :-
   infsuche(Chunks,[]).


% infsuche(+Chunks, -Verbliste) :-
%   sucht in der Liste von Chunks nach Partizipien und
%   Infinitiven

% Infinitiv gefunden
infsuche([chunk(vp(infinit(inf(Inf))))|Chunks], [inf(Inf)|Verbliste]) :-
   infsuche(Chunks,Verbliste),!.

% Partizip gefunden
infsuche([chunk(vp(infinit(partizip(P))))|Chunks], [partizip(P)|Verbliste]) :-
   infsuche(Chunks,Verbliste),!.

% restliche Chunks ignorieren
infsuche([_|Chunks], Verbliste) :-
   infsuche(Chunks,Verbliste).

% Abbruchbedingung: leere Liste
infsuche([],[]).


% dynamische Zaehlvariable
:- dynamic count_aux/1.


% counter(Zaehler) :-
%   setzt Zaehler auf 0 

counter(Zaehler) :-
   count_aux(N),
   Zaehler is 0,
   N > Zaehler,
   retract(count_aux(N)),
   asserta(count_aux(Zaehler)).

counter(0) :- count_aux(0).

counter(_) :- asserta(count_aux(0)).


% count(Zahl) :-
%   Zahl ist die Anzahl der Aufrufe von count/1 

count(Zahl) :-
   retract(count_aux(N)),
   Zahl is N + 1,
   asserta(count_aux(Zahl)).

% tripels(+FormatChunks, -SPO_List) :-
%   ermittelt Subjekt, Praedikat und Objekt aus der Liste FormatChunks,
%   gibt diese in der Liste SPO_List zurueck;
%   setzt die Zaehlvariable count_aux/1 auf Null

tripels(FormatChunks,List) :-
   counter(_),!,
   maplist(tripel,FormatChunks,List).


% tripel(+FormatChunk, -SPO) :-
%   ruft tripel/3 auf ohne Beruecksichtigung des dritten Arguments

tripel(FormatChunk,SPO) :-
   tripel(FormatChunk,SPO,_).


% tripel(+FormatChunk, -SPO, -Type) :-
%   trennt den FormatChunk von seinem Type,
%   ruft tripel_x/3 auf,
%   gibt in SPO den FormatChunk mit den Tags subjekt, praedikat
%   oder objekt zurueck

tripel(FormatChunk,SPO,Type) :-
   FormatChunk =.. [Type|[List]],
   tripel_x(Type,List,SPO),
   !.

% tripel_x(+Type, +List, -SPO) :-
%   benoetigt Type und List,
%   setzt die Tags subjekt, praedikat oder objekt;
%   setzt Tag subjekt nur, wenn die Zaehlvariable count_aux/1
%   auf Null ist

% erste NP = Subjekt
tripel_x(np,List,subjekt(List)) :- count_aux(0), count(_).

% weitere NPs = Objekt
tripel_x(np,List,objekt(List)).

% VP = Praedikat
tripel_x(vp,List,praedikat(List)).

% PP = Objekt
tripel_x(pp,List,objekt(List)).

% restliche Chunktypen ignorieren
tripel_x(_,_,[]).


% ausgabe(+SPO_List, +Zeitform, -Struct, -ZeitStruct) :-
%   ruft ausgabe_x auf und gibt die Ergebnisse an struct weiter,
%   gibt Subjekt, Praedikat und Objekt als Struct zurueck,
%   gibt Subjekt, Praedikat, Objekt und Zeitform als ZeitStruct zurueck,
%   wenn die Variablen ungebunden sind, bekommen sie den String '_'

ausgabe(SPO_List,Zeitform,Struct,ZeitStruct) :-
   ausgabe_x(SPO_List,Praedikat,Subjekt,Objekt),
   ( ( var(Praedikat),P = '_' ); P = Praedikat ),
   ( ( var(Subjekt),  S = '_' ); S = Subjekt   ),
   objekt_ausgabe(Objekt,O),
   ( ( var(Zeitform), Z = '_' ); Z = Zeitform  ),
   !,
   struct([P,S,O,Z],Struct,ZeitStruct).


% ausgabe_x(+SPO_List, -Praedikat, -Subjekt, -Objekt) :-
%  durchsucht die SPO_List nach Praedikat, Subjekt und Objekt und
%  gibt diese in einzelnen Variablen zurueck

% Subjekt gefunden, in Variable "Subjekt" zurueckgeben
ausgabe_x([subjekt(Subjekt)|List],Praedikat,Subjekt,Objekt):-
   ausgabe_x(List,Praedikat,Subjekt,Objekt).
   
% Praedikat gefunden, in Variable "Praedikat" zurueckgeben
ausgabe_x([praedikat(Praedikat)|List],Praedikat,Subjekt,Objekt):-
   ausgabe_x(List,Praedikat,Subjekt,Objekt).

% Objekt gefunden, an ObjektListe anhaengen
ausgabe_x([objekt(Objekt)|List],Praedikat,Subjekt,[Objekt|OL]):-
   ausgabe_x(List,Praedikat,Subjekt,OL).

% nach Objekt folgt ein Praedikat
% beides an ObjektListe anhaengen
ausgabe_x([objekt(Objekt),praedikat(Praed)|List],
          Praedikat,Subjekt,[Praed,Objekt|OL]):-
   ausgabe_x(List,Praedikat,Subjekt,OL).

% Sonderfall / Abbruchbedingung der Rekursion
% wenn ChunkListe leer ist, ist auch die ObjektListe leer
ausgabe_x([],_,_,[]).


% objekt_ausgabe(+ObjektListe, -Objekt) :-
%   behandelt die Ausgabe einer ObjektListe und 
%   gibt diese strukturiert zurueck

% wenn die ObjektListe leer ist, 
% bekommt sie den String '_'
objekt_ausgabe(Objekt,Obj) :-
   Objekt = [],
   Obj = '_'.

% wenn die ObjektListe nur ein Element hat, 
% wird das Element zurueckgegeben
objekt_ausgabe(Objekt,O) :-
   flatten(Objekt,Obj),
   length(Obj,1),
   [O] = Obj,
   !.

% wenn die ObjektListe mehrere Elemente hat, 
% wird dies in Klammern zuruckgegeben
objekt_ausgabe(Objekt,O) :-
   flatten(Objekt,Obj),
   objekt_ausgabe_x(Obj,O).


% objekt_ausgabe_x(+ObjektListe, -ObjektKlammern) :-
%  mehrere Objekte in der ObjektListe werden in Klammern (statt Liste)
%  in ObjektKlammern zurueckgegeben

objekt_ausgabe_x([Objekt|List],(Objekt,O)) :-
   objekt_ausgabe_x(List,O).
   
objekt_ausgabe_x([Objekt],Objekt).


% struct(+Liste,-Struct,-ZeitStruct) :-
%   formt ein Struct um in die Form: Praedikat(Subjekt,Objekt,Zeitform);
%   wenn die ersten drei Elemente der Liste aus den Strings '_' bestehen,
%   wird der String 'kein Tripel moeglich' zurueck gegeben

% kein Tripel moeglich
struct(['_','_','_',_],Ausgabe,Ausgabe) :- 
   Ausgabe = 'kein Tripel moeglich', !.

struct([P,S,O,Z],Struct,ZeitStruct) :-
   ZeitStruct =.. [P,S,O,Z],
   Struct     =.. [P,S,O].

